Note: This tutorial assumes you are comfortable with using roscpp, and have gone through the ROS/Tutorials/CreatingPackage, ROS/Tutorials/WritingPublisherSubscriber(c++) and pluginlib/Tutorials/Writing and Using a Simple Plugin.
(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Writing a C++ Plugin

Description: Shows how to write a plugin for rqt in C++.

Keywords: rqt tutorial

Tutorial Level: INTERMEDIATE

Next Tutorial: rqt/Tutorials/Using .ui file in rqt plugin rqt/Tutorials/Create rqt plugin using an existing Qt based tool

Intro

This tutorial will show you how to write a C++ plugin to integrate a custom user interface into rqt. You're expected to come from Create your new rqt plugin tutorial. Complete example of all files in a package is available on github. If you are using QtCreator you can generate the skeleton code (described below) for the plugin with this rqt plugin wizard.

Particularly in CMakeLists.txt you'll see C++ specifics. Refer to existing plugins that are written in C++ rqt_image_view, rqt_rviz to get a better idea.

Create plugin description file

Basically the same step described in previous tutorial. Some unique cases:

  • Replace rqt_gui_py with rqt_gui_cpp

  • C++ binding specific note for xml attribute:

/library/class@name

  • The name of the plugin that is exported via the PLUGINLIB_DECLARE_CLASS macro.

The code

Create the src/my_namespace/my_plugin.h file and paste the following inside it:

   1 #ifndef my_namespace__my_plugin_H
   2 #define my_namespace__my_plugin_H
   3 
   4 #include <rqt_gui_cpp/plugin.h>
   5 #include <my_namespace/ui_my_plugin.h>
   6 #include <QWidget>
   7 
   8 namespace my_namespace {
   9 
  10 class MyPlugin
  11   : public rqt_gui_cpp::Plugin
  12 {
  13   Q_OBJECT
  14 public:
  15   MyPlugin();
  16   virtual void initPlugin(qt_gui_cpp::PluginContext& context);
  17   virtual void shutdownPlugin();
  18   virtual void saveSettings(qt_gui_cpp::Settings& plugin_settings, qt_gui_cpp::Settings& instance_settings) const;
  19   virtual void restoreSettings(const qt_gui_cpp::Settings& plugin_settings, const qt_gui_cpp::Settings& instance_settings);
  20 
  21   // Comment in to signal that the plugin has a way to configure it
  22   //bool hasConfiguration() const;
  23   //void triggerConfiguration();
  24 private:
  25   Ui::MyPluginWidget ui_;
  26   QWidget* widget_;
  27 };
  28 } // namespace
  29 #endif // my_namespace__my_plugin_H
  30 

Create the src/my_namespace/my_plugin.cpp file and paste the following inside it:

   1 #include "my_plugin.h"
   2 #include <pluginlib/class_list_macros.h>
   3 #include <QStringList>
   4 
   5 namespace my_namespace {
   6 
   7 MyPlugin::MyPlugin()
   8   : rqt_gui_cpp::Plugin()
   9   , widget_(0)
  10 {
  11   // Constructor is called first before initPlugin function, needless to say.
  12 
  13   // give QObjects reasonable names
  14   setObjectName("MyPlugin");
  15 }
  16 
  17 void MyPlugin::initPlugin(qt_gui_cpp::PluginContext& context)
  18 {
  19   // access standalone command line arguments
  20   QStringList argv = context.argv();
  21   // create QWidget
  22   widget_ = new QWidget();
  23   // extend the widget with all attributes and children from UI file
  24   ui_.setupUi(widget_);
  25   // add widget to the user interface
  26   context.addWidget(widget_);
  27 }
  28 
  29 void MyPlugin::shutdownPlugin()
  30 {
  31   // TODO unregister all publishers here
  32 }
  33 
  34 void MyPlugin::saveSettings(qt_gui_cpp::Settings& plugin_settings, qt_gui_cpp::Settings& instance_settings) const
  35 {
  36   // TODO save intrinsic configuration, usually using:
  37   // instance_settings.setValue(k, v)
  38 }
  39 
  40 void MyPlugin::restoreSettings(const qt_gui_cpp::Settings& plugin_settings, const qt_gui_cpp::Settings& instance_settings)
  41 {
  42   // TODO restore intrinsic configuration, usually using:
  43   // v = instance_settings.value(k)
  44 }
  45 
  46 /*bool hasConfiguration() const
  47 {
  48   return true;
  49 }
  50 
  51 void triggerConfiguration()
  52 {
  53   // Usually used to open a dialog to offer the user a set of configuration
  54 }*/
  55 
  56 } // namespace
  57 PLUGINLIB_DECLARE_CLASS(my_namespace, MyPlugin, my_namespace::MyPlugin, rqt_gui_cpp::Plugin)

This simple plugin only adds a single widget using addWidget. But a plugin can contribute multiple widgets by calling that method for each widget. The method can be called at any time to dynamically contribute more widgets. Internally each widget is embedded into a new QDockWidget which itself is added to the QMainWindow.

Using a UI file

The example uses an Qt Designer UI file to describe the widget tree. Instead of hand-coding the widget tree the Qt meta object compiler generates a header file at compile time which includes all the widgets based on the description from the file. For more detail, see rqt/Tutorials/Using .ui file in rqt plugin

Special note for using roscpp with rqt

The plugin should not call init_node as this is performed by rqt_gui_cpp. The plugin can use any roscpp-specific functionality (like Publishers, Subscribers, Parameters). Just make sure to stop timers and publishers, unsubscribe from Topics etc in the shutdown_plugin method.

A C++ plugin inherits from Nodelet and therefore gains the same advantages (i.e. enables exchanging boost>::shared_pointer).

Due to restrictions in Qt, you cannot manipulate Qt widgets directly within ROS callbacks, because they are running in a different thread. In the ROS callback you can:

  • emit a Qt signal (which will bridge across the threads) and manipulate the widgets in the receiving slot

OR

  • only operate on non-widget entities like QAbstractItemModels

Wiki: rqt/Tutorials/Writing a C++ Plugin (last edited 2019-01-30 23:38:18 by martonmiklos)